package jscinttools.utilities.plotterutilities;
import java.lang.String;
//import java.lang.StringBuffer;
import java.lang.Double;
import java.text.DecimalFormat;
import java.awt.FontMetrics;
import java.awt.Font;
import java.awt.Color;
import jhplot.HPlotJa;
import jhplot.jadraw.JaAxes;
import jhplot.jadraw.JaText;
import jplot.Translate;
import japlot.Global;

/**
 *  Class used to generate various PlotterBeautifier
 *
 *@author     Marc-Antoine Verdier
 *@created    20 juillet 2011
 */
public class BeautifierFactory{
	
	private BeautifierFactory(){}
	
	/**
	 *  Generate a PlotterBeautifier for the HPlotJa plotter
	 *
	 *@param  plotter  The plotter
	 *@return          HPlotJa beautifier
	 */
	public static PlotterBeautifier beautifyHPlotJa(HPlotJa plotter){
		return new HPlotJaBeautifierImp(plotter);
	}
}

/**
 *  Description of the Class
 *
 *@author     Marc-Antoine Verdier
 *@created    20 juillet 2011
 */
class HPlotJaBeautifierImp implements PlotterBeautifier{

	private HPlotJa plotter;
	private int nPadX;
	private int nPadY;
	private JaText[] gAxisLabel;
	private JaText[][] padTitleArray;
	private double axesRatio;


	/**
	 *  Constructor for the HPlotJaBeautifierImp object
	 *
	 *@param  plotter  Description of the Parameter
	 */
	public HPlotJaBeautifierImp(HPlotJa plotter){
		this.plotter = plotter;
		this.nPadX = plotter.getNtotX();
		this.nPadY = plotter.getNtotY();
		this.gAxisLabel = new JaText[2];
		this.padTitleArray = new JaText[nPadX][nPadY];
		double sizeX = plotter.getSizeX();
		double sizeY = plotter.getSizeY();
		this.axesRatio = sizeX / sizeY;
		if (nPadX * nPadY == 1)
			usePlotterLabel();
		else
			createLabelCopy();
	}

	private void usePlotterLabel(){
		padTitleArray[0][0] = plotter.getGTitle();
		//gAxisLabel[0] = plotter.getLabelX()[0][0];
		//gAxisLabel[1] = plotter.getLabelY()[0][0];
		gAxisLabel[0] = plotter.getLabelsAll(0)[0][0];
		gAxisLabel[1] = plotter.getLabelsAll(1)[0][0];
	}

	private void createLabelCopy(){
		for (int i = 0; i < nPadX; i++){
			for (int j = 0; j < nPadY; j++){
				padTitleArray[i][j] = copyLabel(plotter.getGTitle());
			}
		}
		//gAxisLabel[0] = copyLabel(plotter.getLabelX()[0][0]);
		//gAxisLabel[1] = copyLabel(plotter.getLabelY()[0][0]);
		gAxisLabel[0] = copyLabel(plotter.getLabelsAll(0)[0][0]);
		gAxisLabel[1] = copyLabel(plotter.getLabelsAll(1)[0][0]);
	}

	private JaText copyLabel(JaText jaLabel){
		String title = jaLabel.getText();
		double xPos = jaLabel.getXndc();
		double yPos = jaLabel.getYndc();
		int rotAngle = jaLabel.getRotAngle();
		JaText jaLabelCopy = new JaText(title, xPos, yPos, "NDC");
		jaLabelCopy.setRotAngle(rotAngle);
		return jaLabelCopy;
	}

	/**
	 *  Description of the Method
	 *
	 *@param  padX  Description of the Parameter
	 *@param  padY  Description of the Parameter
	 *@return       Description of the Return Value
	 */
	public Pad pad(int padX, int padY){
		return new HPlotJaPadImp(padX, padY);
	}

	class HPlotJaPadImp implements Pad{
		
		private int padX;
		private int padY;
		private double padCoordX;
		private double padCoordY;
		private double padWidth;
		private double padHeight;
		private double midPosX;
		private double midPosY;

		/**
		 *  Constructor for the HPlotJaPadImp object
		 *
		 *@param  padX  Description of the Parameter
		 *@param  padY  Description of the Parameter
		 */
		public HPlotJaPadImp(int padX, int padY){
			this.padX = padX;
			this.padY = padY;
			plotter.cd(padX, padY);
			this.padCoordX = plotter.getPadPositionX();
			this.padCoordY = plotter.getPadPositionY();
			this.padWidth = plotter.getPadWidth();
			this.padHeight = plotter.getPadHeight();
			this.midPosX = padCoordX + (0.5 * padWidth);
			this.midPosY = padCoordY + (0.5 * padHeight);
		}

		public PlotLabel title(){
			return new HPlotJaPadTitleImp();
		}

		class HPlotJaPadTitleImp implements PlotLabel{
			
			private JaText padTitle;
			private boolean centered;
			private double distance;

			/**
			 *  Constructor for the HPlotJaPadTitleImp object
			 */
			public HPlotJaPadTitleImp(){
				this.padTitle = padTitleArray[padX - 1][padY - 1];
				this.centered = false;
				this.distance = calcLabelHeight(padTitle) * -1.0;
				plotter.draw(padTitle);
			}

			public void labelText(String label){
				padTitle.setText(label);
				if (centered)
					centerLabel(distance);
			}

			public void changeFont(Font font){
				padTitle.setFont(font);
				if (centered)
					centerLabel(distance);
			}

			public void changeColor(Color color){
				padTitle.setColor(color);
			}

			public void centerLabel(){
				centerLabel(distance);
			}

			public void centerLabel(double distance){
				this.distance = distance;
				this.centered = true;
				double height = calcLabelHeight(padTitle);
				double width = calcLabelWidth(padTitle);
				double xPos = midPosX - (0.5 * width);
				double yPos = padCoordY + distance;
				moveLabel(xPos, yPos);
			}

			public void moveLabel(double xPos, double yPos){
				padTitle.setLocation(xPos, yPos, "NDC");
			}
		}

		public PlotLabel axisLabel(int axis){
			return new HPlotJaPadAxisLabelImp(axis);
		}

		class HPlotJaPadAxisLabelImp implements PlotLabel
		{
			private JaText axisLabel;
			private int axis;
			private boolean centered;
			private double distance;

			/**
			 *  Constructor for the HPlotJaPadAxisLabelImp object
			 *
			 *@param  axis  Description of the Parameter
			 */
			public HPlotJaPadAxisLabelImp(int axis){
				this.axis = axis;
				//if (axis == 0)
				//	this.axisLabel = plotter.getLabelX()[padX - 1][padY - 1];
				//else if (axis == 1)
				//	this.axisLabel = plotter.getLabelY()[padX - 1][padY - 1];
				//else
				//	System.out.println("Wrong Axis !!!");
				this.axisLabel = plotter.getLabelsAll(axis)[padX - 1][padY - 1];
				this.centered = false;
				this.distance = 0.0;
			}

			public void labelText(String label){
				axisLabel.setText(label);
				if (centered)
					centerLabel(distance);
			}

			public void changeFont(Font font){
				axisLabel.setFont(font);
				if (centered)
					centerLabel(distance);
			}

			public void changeColor(Color color){
				axisLabel.setColor(color);
			}

			public void centerLabel(double distance){
				this.distance = distance;
				this.centered = true;
				if (axis == 0)
					movePosX(distance);
				else if (axis == 1)
					movePosY(distance);
			}

			public void centerLabel(){
				centerLabel(0);
			}

			private void movePosX(double distance){
				double height = calcLabelHeight(axisLabel);
				double width = calcLabelWidth(axisLabel);
				double xPos = midPosX - (0.5 * width);
				double yPos = padCoordY + padHeight + 0.05 + height + distance;
				moveLabel(xPos, yPos);
			}

			private void movePosY(double distance){
				double height = calcLabelHeight(axisLabel);
				double width = calcLabelWidth(axisLabel);
				double xPos = padCoordX - 0.075 - distance;
				double yPos = midPosY + (0.5 * width * axesRatio);
				moveLabel(xPos, yPos);
			}

			public void moveLabel(double xPos, double yPos){
				axisLabel.setLocation(xPos, yPos, "NDC");
			}
		}

		public TickLabel tickLabel(int axis){
			return new HPlotJaTickLabelImp(axis);
		}

		class HPlotJaTickLabelImp implements TickLabel{
			
			private String[] tickLabel;
			private JaAxes axes;
			private int axis;

			/**
			 *  Constructor for the HPlotJaTickLabelImp object
			 *
			 *@param  axis  Description of the Parameter
			 */
			public HPlotJaTickLabelImp(int axis){
				plotter.cd(padX, padY);
				this.axis = axis;
				this.axes = plotter.getPad();
				this.tickLabel = axes.getTicLabel()[axis];
			}

			public void removeNegativeZero(){
				for (int i = 0; i < tickLabel.length; i++){
					if (tickLabel[i].equals("-0"))
						tickLabel[i] = "0";
					else if (tickLabel[i].equals("-0.0"))
						tickLabel[i] = "0.0";
					else if (tickLabel[i].equals("-0.00"))
						tickLabel[i] = "0.00";
				}
			}

			public void displayAsDecimal(String format){
				DecimalFormat fmt = new DecimalFormat(format);
				for (int i = 0; i < tickLabel.length; i++){
					double val = Double.valueOf(tickLabel[i]);
					tickLabel[i] = fmt.format(val);
				}
			}

			public void displayAsPowerOfTen(){
				for (int i = 0; i < tickLabel.length; i++){
					if (tickLabel[i].equals("1") || tickLabel[i].equals("1.0") || tickLabel[i].equals("1.00"))
						tickLabel[i] = "10^{0}";
					else if (tickLabel[i].equals("10"))
						tickLabel[i] = "10^{1}";
				}
			}

			public void removeTickLabel(){
				for (int i = 0; i < tickLabel.length; i++){
					tickLabel[i] = "";
				}
			}
			
			public void removeValue(String value){
				for (int i = 0; i < tickLabel.length; i++){
					//if (tickLabel[i] == value)
					if (tickLabel[i].equals(value))
						tickLabel[i] = "";
				}
			}

			public void axisDistance(double axisDistNDC){
				axes.setLabelSpace(axis, axisDistNDC);
			}
		}
	}

	/**
	 *  Description of the Method
	 *
	 *@return    Description of the Return Value
	 */
	public Canvas gCanvas(){
		return new HPlotJaCanvasImp();
	}

	class HPlotJaCanvasImp implements Canvas
	{
		/**
		 *  Constructor for the HPlotJaCanvasImp object
		 */
		public HPlotJaCanvasImp() { }


		public PlotLabel gTitle(){
			return new HPlotJaGTitleImp();
		}

		class HPlotJaGTitleImp implements PlotLabel{
			
			private JaText gTitle;
			private boolean centered;
			private double distance;

			/**
			 *  Constructor for the HPlotJaPadTitleImp object
			 */
			public HPlotJaGTitleImp(){
				this.gTitle = plotter.getGTitle();
				this.centered = false;
				this.distance = calcLabelHeight(gTitle);
			}

			public void labelText(String label){
				gTitle.setText(label);
				if (centered)
					centerLabel(distance);
			}


			public void changeFont(Font font){
				gTitle.setFont(font);
				if (centered)
					centerLabel(distance);
			}

			public void changeColor(Color color){
				gTitle.setColor(color);
			}

			public void centerLabel(){
				centerLabel(distance);
			}

			public void centerLabel(double distance){
				this.distance = distance;
				this.centered = true;
				double height = calcLabelHeight(gTitle);
				double width = calcLabelWidth(gTitle);
				double xPos = 0.5 - (0.5 * width);
				double yPos = 0.005 + distance;
				moveLabel(xPos, yPos);
			}

			public void moveLabel(double xPos, double yPos){
				gTitle.setLocation(xPos, yPos, "NDC");
			}
		}

		public PlotLabel gAxisLabel(int axis){
			return new HPlotJaGAxisLabelImp(axis);
		}

		class HPlotJaGAxisLabelImp implements PlotLabel{
			
			private JaText axisLabel;
			private int axis;
			private boolean centered;
			private double distance;

			/**
			 *  Constructor for the HPlotJaPadAxisLabelImp object
			 *
			 *@param  axis  Description of the Parameter
			 */
			public HPlotJaGAxisLabelImp(int axis){
				this.axis = axis;
				if (axis == 0)
					this.axisLabel = gAxisLabel[0];
				else if (axis == 1)
					this.axisLabel = gAxisLabel[1];
				else
					System.out.println("Wrong Axis !!!");
				this.centered = false;
				this.distance = 0.0;
				plotter.draw(axisLabel);
			}

			public void labelText(String label){
				axisLabel.setText(label);
				if (centered)
					centerLabel(distance);
			}

			public void changeFont(Font font){
				axisLabel.setFont(font);
				if (centered)
					centerLabel(distance);
			}

			public void changeColor(Color color){
				axisLabel.setColor(color);
			}

			public void centerLabel(double distance){
				this.distance = distance;
				this.centered = true;
				if (axis == 0)
					movePosX(distance);
				else if (axis == 1)
					movePosY(distance);
			}

			public void centerLabel(){
				centerLabel(0);
			}

			private void movePosX(double distance){
				double height = calcLabelHeight(axisLabel);
				double width = calcLabelWidth(axisLabel);
				double xPos = 0.5 - (0.5 * width);
				double yPos = 1.0 - distance;
				moveLabel(xPos, yPos);
			}

			private void movePosY(double distance){
				double height = calcLabelHeight(axisLabel);
				double width = calcLabelWidth(axisLabel);
				double xPos = height + distance;
				double yPos = 0.5 + (0.5 * width * axesRatio);
				moveLabel(xPos, yPos);
			}

			public void moveLabel(double xPos, double yPos){
				axisLabel.setLocation(xPos, yPos, "NDC");
			}
		}
	}

	/**
	 *  Calculates the width of the label in NDC coordinates
	 *
	 *@param  label  Description of the Parameter
	 *@return        Description of the Return Value
	 */
	private double calcLabelWidth(JaText label){
		FontMetrics fm = calcFontMetrics(label);
		String stext = Translate.shrink(label.getText());
		int width = fm.stringWidth(stext);
		return Global.toX(width);
	}

	/**
	 *  Calculates the height of the label in NDC coordinates
	 *
	 *@param  label  Description of the Parameter
	 *@return        Description of the Return Value
	 */
	private double calcLabelHeight(JaText label){
		FontMetrics fm = calcFontMetrics(label);
		int height = fm.getHeight();
		return Global.toY(height);
	}

	/**
	 *  Description of the Method
	 *
	 *@param  label  Description of the Parameter
	 *@return        Description of the Return Value
	 */
	private FontMetrics calcFontMetrics(JaText label){
		Font font = label.getFont();
		//System.out.println(font.toString());
		return plotter.getFontMetrics(font);
	}
}
